Core Concepts

Rapid overview

Core Concepts Cheat Sheet

Use these snapshots alongside the prep plan. Each section includes talking points, tips, and quick reminders tailored to the Senior C#/.NET role.

---

Error Handling Philosophy

Defensive Programming vs Fail-Fast

Key principle: Defensive at the edges, fail-fast at the core.

  • Defensive Programming: Anticipate and handle invalid input gracefully at system boundaries
  • Use at: API controllers, external integrations, user input
  • Example: if (price == null || price <= 0) return BadRequest("Invalid price");
  • Keeps system running despite bad input
  • Fail-Fast: Detect invalid states early and throw exceptions immediately in core logic
  • Use in: Domain entities, business rules, internal contracts
  • Example: if (price <= 0) throw new ArgumentException("Price must be positive");
  • Surfaces bugs early for easier debugging

See Defensive Programming vs Fail-Fast for comprehensive examples and decision matrix.

---

Runtime & Language Essentials

.NET Core vs .NET Framework

  • Platform reach: .NET Core/.NET (5+) is cross-platform and ships a self-contained runtime. .NET Framework stays Windows-only and upgrades via the OS.
  • Deployment model: Publish as framework-dependent or self-contained. Self-contained bundles the runtime so each service can run side-by-side (dotnet publish -r linux-x64 --self-contained).
  • Performance tooling:
  • Kestrel is the default cross-platform web server—highlight HTTPS, HTTP/2, and libuv/socket transport choices.
  • GC improvements (background server GC, heap compaction modes) arrive first in .NET Core.
  • Trimming and ReadyToRun images shrink microservices and speed cold starts.
  • When to pick which: Keep legacy WinForms/WPF on .NET Framework; new services, cloud workloads, or containerized apps go with .NET.
  • Example: Containerized pricing API published as self-contained for deterministic runtime, while legacy back-office WinForms app remains on .NET Framework 4.8.
            ┌────────────────────┐
            │   .NET Framework   │  Windows only, machine-wide
            └────────────────────┘
                     ▲
                     │
            ┌────────────────────┐
            │   .NET (Core)      │  Cross-platform, per-app runtime
            └────────────────────┘

CLR & Garbage Collector (GC)

  • Generational model: Short-lived objects die young (Gen 0/1), long-lived promote to Gen 2. Large Object Heap (LOH) stores objects > 85 KB. NET Generational Garbage Collection (GC) Deep Dive.md%20Deep%20Dive.md)
  • Server vs workstation GC: Server GC uses dedicated background threads per core—great for ASP.NET services. Workstation GC favors desktop responsiveness. Server vs Workstation GC.md
  • Allocation discipline: Reduce allocations in hot paths (e.g., reuse buffers, pool objects, prefer Span<T>/Memory<T> to avoid copying).
  • Span<T> usage: Operates on stack or native memory without allocations—perfect for parsing protocol frames or slicing arrays. Remember Span<T> is stack-only; use Memory<T> for async boundaries.
  • Diagram:
Gen0 ──► Gen1 ──► Gen2 ──► LOH
 small    medium   long     massive arrays/strings

Type Choices — class, record, struct, static (where & why)

The detailed guidance for choosing class, record, struct, and static types has been moved to a dedicated sub-note for easier maintenance and reuse:

If you'd like the content kept inline instead, I can revert this change; otherwise the sub-note is ready for expansion.

Async/Await Deep Dive

  • Async/Await Deep Dive reference
  • State machine transformation: The compiler rewrites async methods into a struct-based state machine that awaits continuations. Local variables become fields, so keep them light.
  • Context capture: UI/ASP.NET SynchronizationContext captures by default. Use ConfigureAwait(false) in libraries/background work to avoid deadlocks and excess marshaling.
  • Exception propagation: Exceptions bubble through the returned Task; always await to observe them. For fire-and-forget work, capture exceptions via continuations or hosted services.
  • Deadlock scenario: Blocking on task.Result inside a context that disallows re-entry (e.g., UI thread) stalls the continuation—keep the call chain async end-to-end.
  • Lock coordination: lock is synchronous. Use SemaphoreSlim/AsyncLock when awaiting inside critical sections.
  • Fan-out patterns: Use Task.WhenAll/Task.WhenAny to parallelize I/O and accept CancellationToken all the way through.
  • I/O-bound gains: Use await for database calls, HTTP requests—threads return to the pool while awaiting.
  • Example:
public async Task<Order> PlaceAsync(OrderRequest request)
{
    using var activity = _activitySource.StartActivity("PlaceOrder");
    var quote = await _pricingClient.GetQuoteAsync(request.Symbol)
                                     .ConfigureAwait(false);
    return await _orderGateway.ExecuteAsync(request with { Price = quote })
                              .ConfigureAwait(false);
}

Reflection Basics

  • Reflection Overview
  • When to reach for it: Discover handlers, apply attributes, build dynamic proxies or serializers.
  • Performance tip: Cache PropertyInfo/MethodInfo lookups or emit delegates to avoid repeated reflection overhead.

Value vs Reference Types

  • Stack vs heap: Value types (struct, record struct) live inline; reference types allocate on the heap.
  • Boxing/unboxing: Converting a value type to object boxes; avoid in hot loops (e.g., use generic collections over ArrayList).
  • When to use structs: Keep them small (<16 bytes), immutable, and logically represent a single value (e.g., PriceTick with decimal Bid/Ask).
  • record struct: Offers value semantics with concise syntax and generated equality members.
  • Heap vs stack visualization:
Stack Frame
┌─────────────────────┐
│ PriceTick tick;     │◄─ value copied
└─────────────────────┘

Managed Heap
┌─────────────────────┐
│ Order order ─────┐  │◄─ reference points here
│   Id = 42        │  │
└──────────────────┴──┘

OOP Fundamentals (Encapsulation, Inheritance, Polymorphism, Abstraction)

  • Encapsulation: Hide state, expose behavior with invariants.
  • Inheritance: Use for true "is-a" relationships; avoid deep hierarchies.
  • Polymorphism: Code to interfaces/abstract types to swap behaviors.
  • Abstraction: Define minimal contracts for a concept (e.g., pricing strategy).
  • Example (inheritance + polymorphism):
public abstract class Order
{
    public Guid Id { get; init; }
    public abstract decimal CalculateFees();
}

public sealed class MarketOrder : Order
{
    public decimal Slippage { get; init; }
    public override decimal CalculateFees() => Slippage * 0.1m;
}

public sealed class LimitOrder : Order
{
    public decimal MakerFee { get; init; }
    public override decimal CalculateFees() => MakerFee;
}
  • Rule of thumb: Prefer composition over inheritance when behavior varies at runtime.

Collections & LINQ

  • Deferred execution: Query operators (e.g., Where, Select) defer work until enumerated. Beware of multiple iterations.
  • IEnumerable<T> vs IQueryable<T>: IQueryable<T> builds an expression tree for remote providers (EF Core). Avoid running client-side filters inadvertently.
  • Materialization: Use ToList()/ToArray() when you need a snapshot (e.g., before caching or multi-pass traversal).
  • Avoid multiple enumeration: Cache results with var list = source.ToList(); if you'll iterate twice.
  • Sorted Collections & Interview Talking Points
  • Sorting Algorithms Interview Primer
  • FIFO Queues in .NET
  • Example:
var recentOrders = await _dbContext.Orders
    .Where(o => o.ExecutedAt >= cutoff)
    .OrderByDescending(o => o.ExecutedAt)
    .Take(100)
    .ToListAsync(); // materialize once before logging & response

var topSymbols = recentOrders
    .GroupBy(o => o.Symbol)
    .Select(g => new { Symbol = g.Key, Volume = g.Sum(o => o.Quantity) });
  • Memory-friendly pipeline: Combine Span<T> + LINQ alternatives (ArrayPool<T>, ValueTask) when optimizing allocations.

Architecture & Patterns

  • Clean Architecture
  • Solution Architecture
  • System Architecture
  • SOLID:
  • Single Responsibility: Keep classes focused; e.g., split order validation from execution.
  • Open/Closed: Extend via interfaces/inheritance; plug new execution channels without touching existing code.
  • Liskov Substitution: Ensure derived classes honor base contracts—critical for strategy objects.
  • Interface Segregation: Prefer fine-grained service contracts (e.g., IPriceFeed, ITradeExecutor).
  • Dependency Inversion: Depend on abstractions; mention DI containers.
  • Patterns to Highlight: Strategy (switching trading logic), Observer (market data broadcast), Factory (creating platform-specific handlers), CQRS (command/query split for trading ops), Decorator (enriching services with logging/caching).
  • Decorator Pattern And Decorator wraps services with cross-cutting features like logging, caching, and retry without touching core logic.
  • Factory Pattern The Factory pattern instantiates platform-specific executors like MT4/MT5.
  • Observer Pattern The Observer pattern pushes ticks to multiple subscribers like charts and risk systems.
  • CQRS Pattern CQRS splits commands like ‘PlaceOrder’ from queries like ‘GetOrder’.
  • Strategy Pattern The Strategy pattern lets me swap trading logic dynamically (e.g., aggressive vs passive).

ASP.NET Core & Service Design

  • Pipeline: Middleware order, short-circuiting, exception handling, logging. Mention custom middleware for correlation IDs.
  • Dependency Injection: Service lifetimes—singleton (stateless), scoped (per request), transient (lightweight). Know pitfalls of capturing scoped services in singletons. Dependency Injection Lifetimes at a Glance
  • Controllers & Minimal APIs: Choosing between them, versioning strategies, attribute routing.
  • Resilience: Use Polly for retries/circuit breakers, HttpClientFactory to avoid socket exhaustion, health checks, and structured logging.

Service Architecture Playbook

  • Building Blocks: API gateway, service layer, message broker, background workers, caching, persistence, monitoring.
  • Scalability: Horizontal scaling, stateless services, containerization, readiness/liveness probes.
  • Observability: OpenTelemetry, distributed tracing, correlation IDs, dashboards with Grafana/Kibana.
  • Security: OAuth2/JWT, refresh tokens, rate limiting, data encryption in transit and at rest.

Messaging & Distributed Coordination

  • RabbitMQ: Broker-based AMQP with routing (fanout/topic/direct), acknowledgements, and durable queues. See RabbitMQ Deep Dive for operational notes, pros/cons, and .NET usage.
  • ZeroMQ: Lightweight socket library without broker; great for high-throughput, but requires you to manage topology/reliability.
  • Streaming choices: SSE vs WebSockets for trade-offs between unidirectional streams and full-duplex communication.
  • Delivery Guarantees:
  • At-most-once: Fast, risk of data loss.
  • At-least-once: Requires idempotency.
  • Exactly-once: Difficult; usually simulated with deduplication + idempotent consumers.
  • Idempotency Strategies: Unique message IDs, dedupe stores, database upserts.

Data Layer Essentials

  • SQL: Normalize core entities, use indexes (clustered vs non-clustered), parameterized queries, stored procs for heavy logic. Understand isolation levels (Read Committed default, Snapshot to reduce locks).
  • Query Performance: Use execution plans, avoid SELECT *, prefer filtered indexes. For analytics, mention window functions.
  • Transactions: ACID, handling distributed transactions via outbox pattern.
  • NoSQL:
  • MongoDB: Flexible schema, good for hierarchical data, but design for access patterns.
  • Redis: In-memory cache, data structures (strings, hashes, sorted sets), TTLs, pub/sub, caching patterns (cache-aside, write-through).

Trading & MT4/MT5 Context

  • Platform Overview: MetaTrader platforms for forex/CFD trading. Provide APIs for order execution, account management, and market data.
  • Integration Notes: Connect via bridge services or APIs, handle authentication, manage session state, process asynchronous events.
  • Risk & Latency: Emphasize low-latency data processing, resilient error handling, and compliance logging.
  • Example Use Case: Ingest tick data, normalize, publish via RabbitMQ, and expose aggregated prices through .NET service.

Behavioral Framing

  • Ownership: Highlight times you drove initiatives end-to-end—especially performance optimizations or incident response.
  • Collaboration: Discuss cross-team alignment (e.g., working with QA, DevOps, product).
  • Pressure Handling: Production outages, tight deadlines—focus on calm triage, communication, and retrospectives.
  • Continuous Improvement: Automation efforts, reducing manual ops, writing documentation.

---

Quick Formulas & Snippets

``sql SELECT AccountId, TradeId, Price, ROW_NUMBER() OVER (PARTITION BY AccountId ORDER BY ExecutedAt DESC) AS rn FROM Trades WHERE ExecutedAt >= DATEADD(day, -7, SYSUTCDATETIME()); ``

  • Retry with Polly: Policy.Handle<HttpRequestException>().WaitAndRetryAsync(...)
  • Parallel LINQ Caution: Use PLINQ sparingly; ensure thread safety.
  • SQL Window Example:
  • Cache-Aside Pattern: Try cache, fall back to DB, store result with TTL, handle cache invalidation on writes.

---

Keep this cheat sheet nearby as you run through the plan. Update with your own notes if you have time.

Questions & Answers

Q: When would you choose .NET (Core/5+) over .NET Framework for a service?

A: Default to .NET for any new cross-platform, containerized, or cloud workload because you gain side-by-side deployments, self-contained publishing, trimming, and the latest GC/runtime improvements. Stick with .NET Framework only when you have heavy WinForms/WPF dependencies or Windows-only vendors you cannot port yet.

Q: How do framework-dependent and self-contained deployments change runtime management?

A: Framework-dependent deployments rely on the target machine’s installed runtime, which keeps packages smaller but couples you to host patch cadence. Self-contained deployments bundle the runtime/version with the app (dotnet publish -r linux-x64 --self-contained), increasing size but guaranteeing deterministic behavior and enabling multiple runtime versions per box—ideal for microservices.

Q: Can you explain the generational GC model and when to tune it?

A: The CLR splits the heap into Gen0/Gen1/Gen2 plus the LOH (>85 KB). Short-lived allocations collect in Gen0 and promote when they survive; long-lived objects reside in Gen2/LOH. Tuning usually means enabling server GC for ASP.NET workloads, pinning GC heaps per processor group, or activating background compaction for LOH fragmentation. Manual collections are almost never needed; instead fix allocation pressure (pool buffers, use Span<T>).

Q: What criteria guide you in choosing between class, struct, record, or record struct?

A: Use class for reference semantics, inheritance, or large/mutable state. Choose record when you need concise immutable reference types with value-based equality (DTOs, commands). Reach for struct/record struct for tiny (≤16 bytes), immutable value types that must live inline to avoid allocations (e.g., PriceTick). Avoid large mutable structs since they copy by value and can hurt perf.

Q: How does the async/await state machine affect design choices?

A: Each async method compiles into a struct-based state machine that captures locals as fields and resumes via continuations. That means extra allocations when you capture the context. Use ConfigureAwait(false) in library/background code to skip marshaling to SynchronizationContext, avoid blocking on Task.Result to prevent deadlocks, and prefer async-friendly coordination primitives (SemaphoreSlim, channels).

Q: When do you rely on Span<T>/Memory<T> and what are the constraints?

A: Use Span<T> for high-throughput parsing, slicing, and buffer pooling because it can project stack/heap/native memory without copying. It’s stack-only and cannot escape async boundaries, so wrap it in Memory<T> or ReadOnlyMemory<T> when you need to store it or await between operations. Combine with ArrayPool<T> or pipelines to minimize GC pressure.

Q: How do you avoid accidental boxing and heap churn with value types?

A: Stick to generic collections (List<T>, Dictionary<TKey,TValue>), avoid storing value types as object, and pass them by in reference when appropriate. When you need polymorphism, consider record struct + interfaces or type-safe discriminated unions rather than boxing.

Q: What pitfalls come with ASP.NET Core DI lifetimes?

A: Capturing a scoped service in a singleton causes cross-request state leakage and race conditions. Register lightweight, stateless services as transient; stateful caches/policies as singleton; and request-bound dependencies (DbContext, HttpClient handlers) as scoped. If you need scoped data inside singletons, flow it explicitly (e.g., IHttpContextAccessor, IOptionsSnapshot) or restructure dependencies.

Q: How do you design resilient outbound calls in .NET services?

A: Wrap HTTP/DB/broker calls with HttpClientFactory (typed clients), Polly policies for retries/circuit breakers/timeouts, and structured logging/metrics. Honor CancellationToken, propagate correlation IDs, and keep idempotency keys so at-least-once delivery doesn’t create duplicates. Use Task.WhenAll for fan-out, but cap concurrency via SemaphoreSlim to avoid exhausting sockets.

Q: What strategies ensure messaging idempotency and delivery guarantees?

A: For at-least-once systems like RabbitMQ/Kafka, include message IDs, dedupe tables, or optimistic concurrency checks so replays are safe. Prefer outbox/inbox patterns to bridge DB + queue consistency, and design consumers to handle duplicates (upserts, INSERT ... ON CONFLICT). Exactly-once semantics are simulated through idempotent consumers plus dedupe storage—not by relying on the broker alone.

Q: How do you articulate trade-offs between SQL and NoSQL from these notes?

A: SQL gives strict schema, ACID transactions, mature indexing, and complex queries (window functions). Use it for core trading entities where referential integrity matters. NoSQL (MongoDB, Redis) shines for flexible schemas, caching, and high-throughput document access, but you design for access patterns and enforce consistency in code. Often you pair them—SQL as source of truth, Redis for hot caches—with cache-aside and TTL discipline.

Q: What’s unique about integrating with MT4/MT5 from .NET?

A: MT4/5 expose broker APIs requiring bridge services for order routing and market-data streaming. Emphasize managing session lifecycles, low-latency tick handling, resilience to broker disconnects, and compliance logging. A typical solution ingests ticks, normalizes them, publishes over RabbitMQ, and exposes aggregated data via ASP.NET Core APIs with strict SLAs.